﻿/* ==============================================================================
*
*   Copyright (c) 2010, Pierre Favier
*   All rights reserved.
*
*   NTasking is free software: you can redistribute it and/or modify it
*   under the terms of the GNU General Public License as published by the
*   Free Software Foundation.
*
*   Redistribution and use in source and binary forms, with or without
*   modification, are permitted provided that the following conditions
*   are met:
*
*   -   Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*
*   -   Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials
*       provided with the distribution.
*
*   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
*   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
*   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
*   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
*   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
*   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
*   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
*   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
*   THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*   SUCH DAMAGE.
*
* ============================================================================== */

using System;
using System.Collections.Generic;
using System.Diagnostics;
using NTasking;

namespace DiningPhilosophers
{

    class Program
    {

        static int _diners;
        static int _servings;
        static int[] _eatTimes;
        static int[] _thinkTimes;
        static Process _process;


        static void Profile<T>()
            where T : TableBase, new()
        {
            TableBase table = null;
            TimeSpan totalCPU = TimeSpan.Zero;
            TimeSpan userCPU = TimeSpan.Zero;
            TimeSpan priviledgedCPU = TimeSpan.Zero;
            TimeSpan elapsed = TimeSpan.Zero;
            Stopwatch stopwatch = null;
            Payment[] payments = null;
            int pairCount = 0;
            int singletonCount = 0;
            int requeues = 0;
        
            Ada.Declare(()=>
            {
                table = new T { Diners = _diners, Servings = _servings };
                for (int i = 1; i <= _diners; i++)
                    new Philosopher(table, i, _eatTimes[i], _thinkTimes[i]) { Name = "Philosopher_" + i };
            },
            Begin: ()=>
            {
                stopwatch = Stopwatch.StartNew();
            
                totalCPU = _process.TotalProcessorTime;
                priviledgedCPU = _process.PrivilegedProcessorTime;
                userCPU = _process.UserProcessorTime;

                payments = table.GetBalance();
            
                stopwatch.Stop();
            
                totalCPU = _process.TotalProcessorTime - totalCPU;
                priviledgedCPU = _process.PrivilegedProcessorTime - priviledgedCPU;
                userCPU = _process.UserProcessorTime - userCPU;
                elapsed = stopwatch.Elapsed;
            
                pairCount = table.PairCount();
                singletonCount = table.SingletonCount();
                requeues = table.RequeuesCount();
            });
        
            BuildReport<T>(payments, pairCount, singletonCount, requeues, elapsed, totalCPU, priviledgedCPU, userCPU);
        }


        static void BuildReport<T>(IEnumerable<Payment> payments, int pairCount, int singletonCount, int requeues, TimeSpan elapsed, TimeSpan totalCPU, TimeSpan priviledgedCPU, TimeSpan userCPU)
            where T : TableBase
        {
            //int totalTime = 0;
            int minWait = 1000000000;
            int avgWait = 0;
            int maxWait = 0;

            Console.WriteLine(string.Format("{0} ======================================================", typeof(T).Name));
        
            foreach (Payment p in payments)
            {
                if (p == null) continue;

                Console.WriteLine(String.Format("\tPhilosopher {0}\tCount: {1}\tEat: {2} ms\tThink: {3} ms", p.Id, p.Servings, p.EatTime, p.SleepTime));
                //totalTime += (p.EatTime + p.SleepTime);
                minWait = Math.Min(minWait, p.EatTime + p.SleepTime);
                avgWait += p.EatTime + p.SleepTime;
                maxWait = Math.Max(maxWait, p.EatTime + p.SleepTime);
            }

            Console.WriteLine(string.Format("Total: {0:0.000} ms\tUser: {1:0.000} ms\tSystem: {2:0.000} ms", totalCPU.TotalMilliseconds, userCPU.TotalMilliseconds, priviledgedCPU.TotalMilliseconds));
            Console.WriteLine(string.Format("Requeues: {0}\tPairs: {1}\tSingletons: {2}, Concurrency: {3,6:0.00} %",
                                            requeues, pairCount, singletonCount, pairCount / (double)(pairCount + singletonCount) * 100));
            Console.WriteLine(string.Format("Elapsed: {0} ms\tLoad: {1,6:0.00} %\nMinWait: {2,6:0.00} %\tAvgWait: {3,6:0.00} %\tMaxWait: {4,6:0.00} %",
                                            elapsed,
                                            totalCPU.TotalMilliseconds / elapsed.TotalMilliseconds * 100,
                                            minWait / elapsed.TotalMilliseconds * 100,
                                            avgWait / elapsed.TotalMilliseconds * 100 / _diners,
                                            maxWait / elapsed.TotalMilliseconds * 100));
        }


        static void Main()
        {
            _diners = 5;
            _servings = 100;
            _eatTimes = new[] { 0, 70, 10, 50, 20, 90 };
            _thinkTimes = new[] { 0, 20, 60, 80, 40, 50 };

            _process = Process.GetCurrentProcess();

            Ada.Begin(() =>
            {
                Profile<Table1>();
                Profile<Table2>();
                Profile<Table3>();
                Profile<Table4>();
            });

            Console.ReadKey();
        }

    }
}
